﻿package iamjason.display
{
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.geom.Matrix;
	import flash.geom.Rectangle;
	import flash.utils.Timer;
	import flash.display.DisplayObjectContainer;
	/**
	 * ...
	 * @author Jason Tseng
	 */
	public class AniClipExt extends MovieClip {
		private var _array:Array;
		private var _sampleArray:Array;
		private var _timer:Timer;
		private var _currentFrame:int = 1;
		private var _bit:Bitmap;
		private var _fps:Number = 30;
		private var _destroyed:Boolean = false;
		private var _times:Number = 0;
		private var _totalframes:int;
		private var _sourceframe:int = 1;
		private var _recArray:Array;
		private var _border:Number = 4;
		private var _fpsSet:Boolean = false;
		private var _status:String = "";
		private var _detail:Number = 1;
		private var _ready:Boolean = false;
		
		public function AniClipExt() {
			_totalframes = super.totalFrames;
			this.addEventListener(Event.ADDED_TO_STAGE, addedToStage);
			this.addEventListener(Event.REMOVED_FROM_STAGE, removeFromStage);
			draw();
		}
		public function draw():void {
			if (_bit) {
				try {
					this.removeChild(_bit);
				} catch(e:Error) {}
			}
			_bit = new Bitmap();
			visible = false;
			_recArray = new Array();
			_array = new Array();
			drawSource();
		}
		private function addedToStage(e:Event):void {
			if (!_fpsSet)
				_fps = stage.frameRate;
			init();
		}
		public function init():void {
		}
		private function drawSource():void {
			var _w:Number = this.width;
			var _h:Number = this.height;
			var _matrix:Matrix = new Matrix();
			var _rec:Rectangle = this.getRect(this);
			if (_rec) {
				_w = _rec.width + _border*2;
				_h = _rec.height + _border*2;
				_matrix.translate(-(_rec.x-_border), -(_rec.y-_border));
			}
			_recArray.push(_rec);
			var _bitmap:BitmapData = new BitmapData(_w, _h, true, 0x00000000);
			_bitmap.draw(this, _matrix);
			_array.push(_bitmap);
			if (_sourceframe == _totalframes) {
				drawDone();
				return;
			}
			this.nextFrame();
			_sourceframe ++;
			if (_sourceframe % 20 == 0) {
				var _t:Timer = new Timer(100, 1);
				_t.addEventListener(TimerEvent.TIMER, function(e:TimerEvent):void {
					drawSource();
				} );
				_t.start();
			} else
				drawSource();
		}
		private function drawDone():void {
			var _l:int = this.numChildren;
			while (this.numChildren > 0) {
				this.removeChildAt(0);
				if (_l == this.numChildren)
					break;
				else
					_l = this.numChildren;
			}
			this.addChild(_bit);
			visible = true;
			
			reSample();
			redrawFrame();
			if (_status == "")
				gotoAndPlay(1);
		}
		private function reSample():void {
			if (_detail == 1) {
				_sampleArray = _array;
			} else {
				_sampleArray = new Array();
				for (var i:int = 0; i < _array.length; i++) {
					var _ob:BitmapData = _array[i] as BitmapData;
					var _nb:BitmapData = new BitmapData(_ob.width*_detail, _ob.height*_detail, true, 0x00000000);
					var _matrix:Matrix = new Matrix();
					_matrix.scale(_detail, _detail);
					_nb.draw(_ob, _matrix);
					var _b:BitmapData = new BitmapData(_ob.width, _ob.height, true, 0x00000000);
					_matrix = new Matrix();
					_matrix.scale(1 / _detail, 1 / _detail);
					_b.draw(_nb, _matrix);
					_sampleArray.push(_b);
				}
			}
			_ready = true;
			this.dispatchEvent(new Event(Event.COMPLETE));
		}
		private function destroyTimer():void {
			if (_timer) {
				_timer.stop();
				_timer.removeEventListener(TimerEvent.TIMER, timerEventHandler);
				_timer = null;
			}
			_status = "stop";
		}
		override public function play():void {
			destroyTimer();
			_timer = new Timer(Math.round(1000 / _fps));
			_timer.addEventListener(TimerEvent.TIMER, timerEventHandler);
			_timer.start();
			_status = "play";
		}
		override public function stop():void {
			destroyTimer();
		}
		override public function gotoAndStop(pFrame:Object, scene:String = null):void {
			var _f:Number = Number(pFrame);
			_currentFrame = _f;
			if (_currentFrame > _array.length)
				_currentFrame = _array.length;
			if (_currentFrame < 1)
				_currentFrame = 1;
			destroyTimer();
			redrawFrame();
		}
		override public function gotoAndPlay(pFrame:Object, scene:String = null):void {
			var _f:Number = Number(pFrame);
			_currentFrame = _f;
			if (_currentFrame > _array.length)
				_currentFrame = _array.length;
			if (_currentFrame < 1)
				_currentFrame = 1;
			redrawFrame();
			play();
		}
		override public function nextFrame():void {
			super.nextFrame();
			if (_ready)
				gotoAndStop(_currentFrame+1);
		}
		override public function prevFrame():void {
			super.prevFrame();
			if (_ready)
				gotoAndStop(_currentFrame-1);
		}
		override public function get currentFrame():int { return _currentFrame; }
		override public function get totalFrames():int { return _array.length; }
		
		public function setFPS(pNumber:Number):void {
			_fps = pNumber;
			_fpsSet = true;
			play();
		}
		public function setDetail(pDetail:Number):void {
			_detail = pDetail;
			if (_ready) {
				_ready = false;
				reSample();
				redrawFrame();
			}
		}
		private function timerEventHandler(e:TimerEvent):void {
			if (! _ready)
				return;
			_currentFrame ++;
			if (_currentFrame > _array.length)
				_currentFrame = 1;
			redrawFrame();
		}
		private function redrawFrame():void {
			if (! _ready)
				return;
			var _rec:Rectangle = Rectangle(_recArray[_currentFrame-1]);
			_bit.x = _rec.x - _border;
			_bit.y = _rec.y - _border;
			var _b:BitmapData = _sampleArray[_currentFrame-1] as BitmapData;
			_bit.bitmapData = null;
			_bit.bitmapData = _b;
		}
		
		private function removeFromStage(e:Event):void {
			destroy();
		}
		public function destroy():void {
			if (_destroyed)
				return;
			destroyTimer();
			_bit = null;
			_array = null;
			_recArray = null;
			if (_array) {
				for (var i:int = 0; i < _array.length; i++) {
					BitmapData(_array[i]).dispose();
					try {
						BitmapData(_sampleArray[i]).dispose();
					} catch(e:Error) {}
				}
			}
			var _l:int = this.numChildren;
			while (this.numChildren > 0) {
				this.removeChildAt(0);
				if (_l == this.numChildren)
					break;
				else
					_l = this.numChildren;
			}
			_sampleArray = null;
			if (parent) {
				try {
					DisplayObjectContainer(parent).removeChild(this);
				} catch(e:Error) {}
			}
		}
	}

}